package com.douyin.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.douyin.dto.LoginFormDTO;
import com.douyin.dto.ResetPasswordDTO;
import com.douyin.dto.Result;
import com.douyin.dto.UserUpdateProfileDTO;
import com.douyin.entity.User;
import com.douyin.mapper.UserMapper;
import com.douyin.properties.JwtProperties;
import com.douyin.service.EmailService;
import com.douyin.service.IUserService;
import com.douyin.utils.*;
import com.douyin.vo.UserVO;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    EmailService emailService;
    @Autowired
    UserMapper userMapper;
    @Autowired
    JwtProperties jwtProperties;

    @Override
    public Result sendCode(String email) {
        //检查邮箱格式是否正确
        boolean valid = RegexUtils.isEmailInvalid(email);
        if (valid) {
            return Result.fail("邮箱格式错误");
        }
        //邮箱格式正确检查redis是否存在该邮箱验证码
        String emailKey = RedisConstants.LOGIN_CODE_KEY + email;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(emailKey))) {
            return Result.fail("验证码已发送");
        }
        //生成验证码
        String code = VerificationCodeGenerator.generateCodeV2();
        //将验证码存入redis
        redisTemplate.opsForValue().set(emailKey, code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //发送验证码
        emailService.sendVerificationCode(email, code);

        return Result.ok("验证码发送成功");
    }

    @Override
    public Result login(LoginFormDTO loginForm) {
        // 1. 基本参数校验
        if (loginForm == null || StrUtil.isBlank(loginForm.getEmail())) {
            return Result.fail("请输入邮箱地址");
        }
        String email = loginForm.getEmail();
        User user;

        // 2. 判断是哪种登录方式：密码登录
        if (StrUtil.isNotBlank(loginForm.getPassword())) {
            // --- 密码登录逻辑 ---

            // a. 根据邮箱查询用户
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getEmail, email).eq(User::getStatus, 1);
            user = userMapper.selectOne(queryWrapper);

            // b. 判断用户是否存在
            if (user == null) {
                return Result.fail("用户不存在或密码错误");
            }

            // c. 校验密码 (!!! 安全警告 !!!)
            // 在真实项目中，绝不能明文存储和比较密码！
            // 必须使用 BCryptPasswordEncoder 等工具对数据库中的密码进行加密存储。
            // 正确的校验方式应该是：passwordEncoder.matches(loginForm.getPassword(), user.getPassword())
            String password = user.getPassword();
            Boolean matches = PasswordEncoder.matches(password, loginForm.getPassword());
            if (!matches) {
                return Result.fail("用户不存在或密码错误");
            }

            // 密码验证通过，user对象已准备好，将进入下方的“统一成功处理”部分

        }
        // 3. 判断是哪种登录方式：验证码登录
        else if (StrUtil.isNotBlank(loginForm.getCode())) {
            // --- 验证码登录逻辑 ---

            // a. 从 Redis 中获取验证码
            String emailKey = RedisConstants.LOGIN_CODE_KEY + email;
            String cachedCode = redisTemplate.opsForValue().get(emailKey);

            // b. 校验验证码
            if (StrUtil.isBlank(cachedCode) || !cachedCode.equals(loginForm.getCode())) {
                return Result.fail("验证码错误或已失效");
            }

            // c. 验证码正确，根据邮箱查询用户
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getEmail, email).eq(User::getStatus, 1);
            user = userMapper.selectOne(queryWrapper);

            // d. 如果用户不存在，则自动为其注册一个新账号（登录注册一体化）
            if (user == null) {
                user = new User();
                user.setEmail(email);
                user.setNickname("user_" + RandomUtil.randomString(6)); // 生成一个随机昵称
                // TODO: 设置默认密码、头像等其他信息
                userMapper.insert(user);
            }

            // 验证码验证通过，user对象已准备好，将进入下方的“统一成功处理”部分
            // 登录成功后，从Redis中删除验证码，防止重复使用
            redisTemplate.delete(emailKey);

        }
        // 4. 如果密码和验证码都为空
        else {
            return Result.fail("请输入密码或验证码");
        }

        // 5. 统一成功处理：下发 JWT 令牌
        // 此时，无论是密码登录还是验证码登录，我们都有了一个有效的 `user` 对象
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("email", user.getEmail());
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);

        return Result.ok(token);
    }

    @Override
    public Result logout(String token) {
        if (StrUtil.isBlank(token)) {
            return Result.ok(); // 如果没有token，直接认为登出成功
        }

        // 2. 解析 Token，获取其 claims
        Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
        if (claims == null) {
            // Token 本身就无效或已过期，也视为登出成功
            return Result.ok();
        }

        // 3. 将 Token 放入 Redis 黑名单
        // a. 计算该 Token 剩余的有效时间
        long remainingMillis = claims.getExpiration().getTime() - System.currentTimeMillis();

        // b. 如果 Token 已经过期或即将过期，没必要放入黑名单
        if (remainingMillis > 0) {
            // c. 使用 Token 本身作为 Key，存入 Redis，并设置其过期时间为 Token 的剩余有效时间
            // 这样 Redis 会自动清理掉已经自然过期的黑名单条目，避免了黑名单无限增长
            String blacklistedKey = RedisConstants.BLACKLIST_TOKEN_KEY + token;
            redisTemplate.opsForValue().set(blacklistedKey, "1", remainingMillis, TimeUnit.MILLISECONDS);
        }

        // （可选）清理 BaseContext，虽然拦截器会做，但这里做了也无妨
        BaseContext.removeCurrentId();

        return Result.ok("登出成功");
    }

    @Override
    public UserVO me() {
        // 获取当前用户ID
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            return null;
        }

        // 根据用户ID查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        // 将用户信息转换为 VO
        BeanUtils.copyProperties(user, userVO);

        return userVO;
    }

    /**
     * 修改用户个人信息
     * @param userUpdateProfileDTO
     * @return
     */
    @Override
    public Result updatePersonalInfo(UserUpdateProfileDTO userUpdateProfileDTO) {
        // 获取当前用户ID
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            return Result.fail("用户未登录");
        }

        // 根据用户ID查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            return Result.fail("用户不存在");
        }

        // 更新用户信息
        BeanUtils.copyProperties(userUpdateProfileDTO, user);
        userMapper.updateById(user);

        return Result.ok();
    }

    /**
     * 修改密码
     * @param resetPasswordDTO
     * @return
     */
    @Override
    public Result updatePassword(ResetPasswordDTO resetPasswordDTO) {
        // 获取当前用户ID
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            return Result.fail("用户未登录");
        }

        // 根据用户ID查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            return Result.fail("用户不存在");
        }

        if (resetPasswordDTO==null){
            return Result.fail("参数错误");
        }
        if (resetPasswordDTO.getCode().isEmpty()||resetPasswordDTO.getPassword().isEmpty()||resetPasswordDTO.getCheckPassword().isEmpty()){
            return Result.fail("参数错误");
        }
            if (!resetPasswordDTO.getPassword().equals(resetPasswordDTO.getCheckPassword())){
            return Result.fail("两次密码不一致");
        }
        // 校验验证码
        String emailKey = RedisConstants.LOGIN_CODE_KEY + user.getEmail();
        String cachedCode = redisTemplate.opsForValue().get(emailKey);
        if (StrUtil.isBlank(cachedCode) || !cachedCode.equals(resetPasswordDTO.getCode())) {
            return Result.fail("验证码错误或已失效");
        }
        // 校验验证码通过，加密密码
        String encode = PasswordEncoder.encode(resetPasswordDTO.getPassword());
        user.setPassword(encode);
        userMapper.updateById(user);
        return Result.ok("修改成功");
    }
}



